/* FILE:   uart.c
 * AUTHOR: Evan Zimmerman <zimmermane@msoe.edu> 
 * PURPOSE: This file contains the functions needed for
 *          UART communication, including a UART 
 *          initialization method and get and put
 *          character methods
 */

#include "uart.h"
#include <avr/io.h>
#include <stdio.h>

volatile char RX_BUFF[MAX_BUFFER_SIZE];
volatile char* rxptr;
volatile char* cptr;

/* uart_init
 * 
 * PURPOSE:
 *    Initializes the UART for sending and receiving data.
 *    The value to be written to URBRRL is passed in, and it
 *    is assumed that *only UBRRL needs to be written to.
 *    These values can be found in the Atmega32 reference
 *    manual. Initialized for synchronous communication
 *    and 8-bit characters.
 * PARAMS:
 *    uint8_t ubrrVal - value to be written to UBRRL
 * RETURNS:
 *    none
 */
void uart_init(uint8_t ubrrVal){
	// sets the desired baud rate
	UBRRL=ubrrVal;
	UCSRA = 0;
	
	// enable transmit and receive
	UCSRB = (1<<TXEN)|(1<<RXEN);
	
	// synchronous operation, 8-bit char size
	UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);

	// initialize pointers to 0
	rxptr = 0;
	cptr = 0;
	return;
}

/* uart_putc
 * 
 * PURPOSE:
 *    Puts the specified character into the UDR to be
 *    sent out. If the char is a newline, it also sends
 *    out the return character.
 * PARAMS:
 *    char c - character to be written to the UDR
 * RETURNS:
 *    none
 */
void uart_putc(char c){

	// wait here until the UDR is empty
	while(!(UCSRA&(1<<UDRE)));

	// add the char to the UDR
	UDR = c;

	// if the char is a newline, also send return
	if(c == '\n'){
		uart_putc('\r');
	}
	return;
}

/* uart_getc
 * 
 * PURPOSE:
 *    Gets a character from the UDR and echos it back.
 *    The return character resets the buffer. If the char 
 *    is printable, it gets echoed back unless the buffer 
 *    is full, in which case a 'beep' char is sent.
 *    Backspace overwrites the previous char with a space
 *    and decrements the buffer write pointer.
 * PARAMS:
 *    none
 * RETURNS:
 *    char c - the character that was read
 */
char uart_getc(){
	char c;
	// if the start of a new line
	if(rxptr == 0){
		// initialize buffer write pointer to start of buffer
		for(cptr = RX_BUFF;;){
			// poll for new character
			while(!(UCSRA&(1<<RXC)));
			c = UDR;
			// if the char is a return, replace with newline,
			// increment the pointer, send the newline, reset
			// the read pointer and break the loop
			if(c=='\r'){
				c = '\n';
				*cptr = c;
				cptr++;

				uart_putc(c);
				rxptr = RX_BUFF;
				break;
			}
			// if char is printable
			if((c >= ' ') && (c < 0x7F)){
				// if the buffer is full, send a beep to the terminal
				if(cptr == RX_BUFF + MAX_BUFFER_SIZE - 2){
					uart_putc('\a'); // beep
				}
				// otherwise set the char, increment the pointer, and send it
				else{
					*cptr = c;
					cptr++;
					uart_putc(c);
				}

			}
			// if backspace or delete
			if((c == 0x08) || (c == 0x7F)){
				// if the write pointer is not at the start of the buffer
				if(cptr > RX_BUFF){
					uart_putc(0x08); // send backspace
					uart_putc(' ');  // send space to overwrite previous char
					uart_putc(0x08); // send backspace
					cptr--; // decrement the buffer write pointer
				}
			}
		}
	}
	// get the character
	c = *rxptr;
	// increment the read pointer
	rxptr++;
	// if the char was a newline, reset the read pointer to 0
	if(c == '\n'){
		rxptr = 0;
	}
	// return the char
	return c;
}